#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <memory>
#include <sys/epoll.h>
#include "easysock.hpp"
#include "easylog.hpp"

namespace server
{

const int   default_size    = 20;
const int   default_timeout = -1;
const int   default_fd      = -1;

class epoll_server : public inet::tcp::server
{
public:
    epoll_server(uint16_t port) 
        : server(port), _epfd(default_fd), _timeout(default_timeout), _epevs(new struct epoll_event[default_size])
    {
        _epfd = epoll_create(128);

        struct epoll_event epev;
        epev.events = EPOLLIN;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, _sock, &epev);
    }

    ~epoll_server() { close(_epfd); }

    void start()
    {
        while (true)
        {
            int timeout = _timeout;
            int n = epoll_wait(_epfd, _epevs.get(), default_size, timeout);

            switch (n)
            {
            case 0:
                INFO("time out: %d", timeout);
                break;
            case -1:
                ERROR("select error, %d %s", errno, strerror(errno));
                break;
            default:
                event_handler(n);
                break;
            }
        }
    }

private:
    void event_handler(int size)
    {
        for (int i = 0; i < size; i++)
        {
            auto& events = _epevs[i].events;
            auto& fd = _epevs[i].data.fd;

            if (events & EPOLLIN)
            {
                if (fd == _sock)
                {
                    acceptor();
                }
                else
                {
                    std::string buf;
                    recver(fd, &buf);
                }
            }
            // if (events & EPOLLOUT)
            // {
            //     std::string msg = "test";
            //     sender(fd, msg);
            // }
            if (events & EPOLLERR)
            {
                WARN("excepton event occurred, fd: %d", fd);
            }
        }
    }

    void acceptor()
    {
        std::string cip;
        uint16_t cport;
        int newfd = accept(&cip, &cport);

        if (0)
        {
            close(newfd);
            WARN("connect close, fd array is full");
            return;
        }

        struct epoll_event epev;
        epev.events = EPOLLIN | EPOLLOUT;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, newfd, &epev);

        INFO("a connect %d has been accepted [%s:%d]", newfd, cip.c_str(), cport);
    }

    void recver(int fd, std::string* buf)
    {
        ssize_t s = recv(fd, buf, 1024);
        if (s > 0)
        {
            std::cout << *buf << std::endl;
        }
        else
        {
            if (s == 0) INFO("client quit");
            else WARN("recv error, %d %s", errno, strerror(errno));

            close(fd);
        }
    }

    void sender(int fd, const std::string& msg)
    {
        size_t s = send(fd, msg);

        if (s <= 0)
        {
            if (s == 0) INFO("client quit");
            else WARN("send error, %d %s", errno, strerror(errno));

            close(fd);
        }
    }

private:
    std::unique_ptr<struct epoll_event[]> _epevs;
    int _epfd;
    int _timeout;
};

} // namespace server
